package com.plugin.storage.storage.model;

import com.google.common.base.Strings;
import com.plugin.storage.base.SdkClient;
import com.plugin.storage.base.SdkContext;
import com.plugin.storage.base.StorageException;
import com.plugin.storage.storage.StorageUtils;
import com.plugin.storage.storage.spi.UploadProvider;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.multipart.MultipartFile;

import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.util.Date;

import static com.google.common.io.Files.*;
import static com.plugin.storage.storage.StorageUtils.getEndpoint;

public class UploadFileReq extends SdkClient {

    private static final Logger LOGGER = LoggerFactory.getLogger(UploadFileReq.class);

    /**
     * bucket名称
     */
    private String bucket;

    /**
     * 本地文件路径
     */
    private String localPath;

    /**
     * 本地文件
     */
    private File localFile;

    /**
     * springmvc 上传文件
     */
    private MultipartFile multipartFile;

    /**
     * 指定key
     */
    private String key;

    /**
     * 上传/下载目录
     */
    private String targetDir;

    /**
     * 文件url
     */
    private String url;

    /**
     * 元数据
     */
    private Meta meta;

    public UploadFileReq() {
    }

    public UploadFileReq(File localFile) {
        this.localFile = localFile;
    }

    public UploadFileReq(MultipartFile multipartFile) {
        this.multipartFile = multipartFile;
    }

    public String getBucket() {
        return bucket;
    }

    public UploadFileReq bucket(String bucket) {
        this.bucket = bucket;
        return this;
    }

    public String getLocalPath() {
        return localPath;
    }

    public UploadFileReq path(String localPath) {
        this.localPath = localPath;
        return this;
    }

    public File getLocalFile() {
        return localFile;
    }

    public UploadFileReq setLocalFile(File localFile) {
        this.localFile = localFile;
        return this;
    }

    public MultipartFile getMultipartFile() {
        return multipartFile;
    }

    public UploadFileReq setMultipartFile(MultipartFile multipartFile) {
        this.multipartFile = multipartFile;
        return this;
    }

    public String getTargetDir() {
        return targetDir;
    }

    public UploadFileReq setTargetDir(String targetDir) {
        if (targetDir.startsWith("/")) {
            targetDir = targetDir.substring(1);
        }
        if (targetDir.endsWith("/")) {
            targetDir = targetDir.substring(0, targetDir.length() - 1);
        }
        this.targetDir = targetDir;
        return this;
    }

    public UploadFileReq dir(String targetDir) {
        return setTargetDir(targetDir);
    }

    public String getKey() {
        return key;
    }

    public UploadFileReq setKey(String key) {
        this.key = key;
        return this;
    }

    public String getUrl() {
        return url;
    }

    public UploadFileReq setUrl(String url) {
        if (!Strings.isNullOrEmpty(url)) {

        }
        this.url = url;
        return this;
    }

    public Meta getMeta() {
        return meta;
    }

    public UploadFile upload() {
        UploadProvider uploadProvider = getUploadProvider();
        SdkContext sdkContext = getContext(bucket);
        String fileName = StorageUtils.getFileName();
        if (localFile != null) {
            localPath = localFile.getAbsolutePath();
            fileName = fileName + "." + getFileExtension(localPath).toLowerCase();
            detectMeta(localFile);
        }
        if (multipartFile != null) {
            try {
                String suffix = getSuffix(multipartFile);
                File tempFile = File.createTempFile(fileName, suffix);
                tempFile.deleteOnExit();
                multipartFile.transferTo(tempFile);
                localPath = tempFile.getAbsolutePath();
                detectMeta(tempFile).setOriginFileName(multipartFile.getOriginalFilename());
                fileName = fileName + suffix;
            } catch (IOException e) {
                LOGGER.error(e.getMessage(), e);
            }
        }
        if (Strings.isNullOrEmpty(key)) {
            key = getDir(targetDir) + fileName;
        }
        return uploadProvider.upload(sdkContext, this);
    }

    public UploadFileReq setMeta(Meta meta) {
        this.meta = meta;
        return this;
    }

    public UploadFile download() {
        UploadProvider uploadProvider = getUploadProvider();
        SdkContext sdkContext = getContext(bucket);
        try {
            if (!Strings.isNullOrEmpty(targetDir)) {
                localPath = getDir(targetDir) + key;
            }
            if (Strings.isNullOrEmpty(localPath)) {
                File tempFile = File.createTempFile(getNameWithoutExtension(key), "." + getFileExtension(key));
                tempFile.deleteOnExit();
                localPath = tempFile.getAbsolutePath();
            }
            localFile = new File(localPath);
            createParentDirs(localFile);
        } catch (IOException e) {
            LOGGER.error(e.getMessage(), e);
        }
        return uploadProvider.download(sdkContext, this);
    }

    public UploadFile.Auth auth() {
        UploadProvider uploadProvider = getUploadProvider();
        SdkContext sdkContext = getContext(bucket);
        if (Strings.isNullOrEmpty(targetDir) && Strings.isNullOrEmpty(key)) {
            throw new StorageException("未指定上传路径");
        }
        return uploadProvider.auth(sdkContext, this);
    }

    public String url() {
        UploadProvider uploadProvider = getUploadProvider();
        SdkContext sdkContext;
        if (!Strings.isNullOrEmpty(url)) {
            String endpoint = getEndpoint(url);
            sdkContext = getContextByEndPoint(endpoint);
            key = StorageUtils.getKey(url);
        } else {
            sdkContext = getContext(bucket);
        }
        return uploadProvider.url(sdkContext, this);
    }

    public String saveUrl() {
        return StorageUtils.removeQuery(url());
    }

    private String getSuffix(MultipartFile multipartFile) {
        String fileName = multipartFile.getOriginalFilename();
        if (Strings.isNullOrEmpty(fileName)) {
            return "";
        }
        return "." + getFileExtension(fileName).toLowerCase();
    }

    private String getDir(String targetDir) {
        targetDir = Strings.isNullOrEmpty(targetDir) ? "default/" : targetDir;
        if (!targetDir.endsWith("/")) {
            targetDir = targetDir + "/";
        }
        return targetDir;
    }

    private Meta detectMeta(File file) {
        Meta meta = new Meta();
        try {
            meta.setContentType(Files.probeContentType(file.toPath()));
        } catch (IOException e) {
            LOGGER.error(e.getMessage(), e);
        }
        meta.setLastModified(new Date(file.lastModified()));
        meta.setOriginFileName(file.getName());
        setMeta(meta);
        return meta;
    }

    public static class Meta {

        private String contentType;

        private String contentEncoding;

        private Date lastModified;

        private String crc64;

        private String originFileName;

        public String getContentType() {
            return contentType;
        }

        public Meta setContentType(String contentType) {
            this.contentType = contentType;
            return this;
        }

        public String getContentEncoding() {
            return contentEncoding;
        }

        public Meta setContentEncoding(String contentEncoding) {
            this.contentEncoding = contentEncoding;
            return this;
        }

        public Date getLastModified() {
            return lastModified;
        }

        public Meta setLastModified(Date lastModified) {
            this.lastModified = lastModified;
            return this;
        }

        public String getCrc64() {
            return crc64;
        }

        public Meta setCrc64(String crc64) {
            this.crc64 = crc64;
            return this;
        }

        public String getOriginFileName() {
            return originFileName;
        }

        public Meta setOriginFileName(String originFileName) {
            this.originFileName = originFileName;
            return this;
        }
    }
}
